#!/bin/sh
# shellcheck source=/dev/null
# shellcheck disable=1090

# script for udhcpc
# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
# Copyright (c) 2020 Cem Keylan <cem@ckyln.com>

# I have made some minor changes, POSIX shell fixes, and linting.
# Rest of the script is pretty much the same.
# - Cem

[ -f "${UDHCPC:=/etc/udhcpc.d}/udhcpc.conf" ] && . "$UDHCPC/udhcpc.conf"

RESOLV_CONF="/etc/resolv.conf"

# Export variables so that hooks can also make use of them.
export broadcast
export dns
export domain
export interface
export ip
export mask
export metric
export router
export search
export subnet

run_scripts() {
    for file in "$1/"*; do
        [ -f "$file" ] && "$file"
    done
}

deconfig() { ip -4 addr flush dev "$interface" ;}

is_wifi() { [ -e "/sys/class/net/$interface/phy80211" ] ;}

if_index() {
    if [ -e "/sys/class/net/$interface/ifindex" ]; then
	read -r ifindex < "/sys/class/net/$interface/ifindex"
    else
	ifindex="$(ip -4 link show dev "$interface")"
    fi
    printf '%s\n' "${ifindex%%:*}"
}

calc_metric() {
    is_wifi && { printf '%s\n' "$(( 300 + $(if_index) ))"; return ;}
    printf '%s\n' "$(( 200 + $(if_index) ))"
}

routes() {
    [ "$router" ] || return
    for i in $NO_GATEWAY; do
	[ "$i" = "$interface" ] && return
    done
    while ip -4 route del default via dev "$interface" 2>/dev/null; do
	:
    done
    num=-1
    for gw in $router; do
	if [ "$subnet" = "255.255.255.255" ]; then
	    # special case for /32 subnets:
	    # /32 instructs kernel to always use routing for all outgoing packets
	    # (they can never be sent to local subnet - there is no local subnet for /32).
	    # Used in datacenters, avoids the need for private ip-addresses between two hops.
	    ip -4 route add "$gw" dev "$interface"
	fi
	ip -4 route add 0.0.0.0/0 via "$gw" dev "$interface" \
	   metric $(( (num += 1) + ${IF_METRIC:-$(calc_metric)} ))
    done
}

resolvconf() {
    case "$IF_PEER_DNS" in yes|'') ;; *) return; esac
    case "$RESOLV_CONF" in [Nn][Oo]|'') return; esac
    case " $NO_DNS " in *" $interface "*) return; esac
    :> "$RESOLV_CONF.$$"
    if [ "$search" ]; then
        printf 'search %s\n' "$search" >> "$RESOLV_CONF.$$"
    elif [ "$domain" ]; then
        printf 'search %s\n' "$domain" >> "$RESOLV_CONF.$$"
    fi

    # Word-splitting is intentional
    # shellcheck disable=2086
    printf 'nameserver %s\n' $dns >> "$RESOLV_CONF.$$"
    chmod a+r "$RESOLV_CONF.$$"
    mv "$RESOLV_CONF.$$" "$RESOLV_CONF"
}

bound() {
    ip -4 addr add "$ip/$mask" ${broadcast:+broadcast $broadcast} dev "$interface"
    ip -4 link set dev "$interface" up
    routes
    resolvconf
}

renew() {
    ip -4 addr show dev "$interface" | grep -q "$ip/$mask" || {
        ip -4 addr flush dev "$interface"
        ip -4 addr add "$ip/$mask" ${broadcast:+broadcast $broadcast} dev "$interface"
    }
    for i in $router; do
        ip -4 route show | grep ^default | grep -q "$i" || {
            routes
            break
        }
    done
    if ! grep -q "^search $domain" "$RESOLV_CONF" ; then
	resolvconf
	return
    fi
    for i in $dns; do
	if ! grep -q "^nameserver $i" "$RESOLV_CONF" ; then
	    resolvconf
	    return
	fi
    done
}

case "$1" in
    deconfig|renew|bound)
	run_scripts "$UDHCPC/pre-$1"
	$1
	run_scripts "$UDHCPC/post-$1"
	;;
    leasefail)
	echo "udhcpc failed to get a DHCP lease" >&2
	;;
    nak)
	echo "udhcpc received DHCP NAK" >&2
	;;
    *)
	echo "Error: this script should be called from udhcpc" >&2
	exit 1
	;;
esac
exit 0
